package bot.module.th.estimation;

import game.gamestorage.texas.db.estimation.GetterQueryExecution;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import util.DebugOut;

/**
 * This class initializes the estimation of x opponents for a given amount of competitions<br>
 * It holds the estimation data in the Map opponentsEstimations</br>
 * - BY NOW ONLY THE ESTIMATION OF THE SPECIAL ROUND "PREFLOPSHC" IS EXEMPLARILY IMPLEMENTED -
 *         
 * @author Witthold/Korol
 */
public class Estimation implements IF_Estimation {

	private String myName;
	long timestamp;
	/** if players were explicitly announced, estimate only those */
	private List<String> playersExplicitely;
	private int[] completedCompetitionsIds;
	private int competitionId;
	/** 
	 * Map holding all estimations by opponent and competition</br>
	 * playerId, HashMap oppPerCompetition: competitionId, estimation 
	 */
	private Map<Integer, Map<Integer, Opponent>> opponentsEstimations = new HashMap<Integer, Map<Integer, Opponent>>();


	/**
	 * Constructor
	 * @param myName - bots name
	 * @param beginAt -  - ability to reduce estimation of completed competitions to specific date (timestamp)
	 * @param restrictToPlayers - ability to reduce estimation of completed competitions to specific players
	 */
	public Estimation(String myName, long beginAt, List<String> restrictToPlayers) {

		this.myName = myName;
		this.timestamp = beginAt;
		this.playersExplicitely = restrictToPlayers;
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_Estimation#estimateCompletedCompetitions(int)
	 */
	public void estimateCompletedCompetitions(int gamedef_id) {
		
		long start = System.currentTimeMillis();
		
		/* Prerequisites */
		setCompletedCompetitionsIds(gamedef_id, timestamp);
		setOpponentsEstimations();

		/* estimate() */
		for (Integer player : opponentsEstimations.keySet()) {
			for (Integer comp : opponentsEstimations.get(player).keySet()) {
				opponentsEstimations.get(player).get(comp).estimate();
			}
		}
		
		long finished = System.currentTimeMillis();
		DebugOut.showVerboseMain("Total time for estimating " + completedCompetitionsIds.length + " completed competitions: " + (finished - start));
	}


	/**
	 * get all completed competitions with current gamedef
	 * 
	 * @param gamedef_id - already generated by db
	 */
	private void setCompletedCompetitionsIds(int gamedef_id, long timestamp) {

		/* get the requested completedCompetitionsIds */
		completedCompetitionsIds = GetterQueryExecution.getCompetitionIds(gamedef_id, timestamp);
		for (int i = 0; i < completedCompetitionsIds.length; i++) {
			DebugOut.showVerboseEstimation("completedCompetitionsIds: " + completedCompetitionsIds[i]);
		}
		DebugOut.showVerboseEstimation("completedCompetitionsIds.length: " + completedCompetitionsIds.length);

		/* case no newer competitions than timestamp */
		if (completedCompetitionsIds.length < 1) {
			DebugOut.showVerboseEstimation("no evaluating of completed competitions needed");
		}
	}


	/**
	 * fill opponentsAtCompetition with completed competitions by playerId, competitionId
	 */
	private void setOpponentsEstimations() {

		/* instanciate Estimation by player and competition */
		for (int i = 0; i < completedCompetitionsIds.length; i++) {
			/* String playerName, playerId */
			Map<String, Integer> playersOfCompletedCompetition = GetterQueryExecution.getPlayersInCompetitionSI(completedCompetitionsIds[i]);

			for (Map.Entry<String, Integer> entry : playersOfCompletedCompetition.entrySet()) {
				/*
				 * if players were explicitly announced in Constructor, estimate only those
				 * do not estimate myself
				 */
				String name = entry.getKey();
				if (playersExplicitely.size() > 0 && !playersExplicitely.contains(name) || name.equals(myName)) {
				} else {
					// add to HashMap for estimation
					addOpponentWithCompetition(entry.getValue(), completedCompetitionsIds[i]);
				}
			}
		}
	}


	/**
	 * add opponent with competition to Map opponentsEstimations
	 *  
	 * @param player_id
	 * @param competition_id already generated by db
	 * @return competition is new to Map opponentsEstimations
	 */
	private boolean addOpponentWithCompetition(int player_id, int competition_id) {

		if (opponentsEstimations.containsKey(player_id)) {
			if (!opponentsEstimations.get(player_id).containsKey(competition_id)) { // new competition
				// add to HashMap for estimation and instanciate opponent
				opponentsEstimations.get(player_id).put(competition_id, new Opponent(player_id, competition_id)); 
			} else {
				System.err.println("Estimation.addOpponentWithCompetition: Should never reach here. player_id, comp_id: " + player_id
						+ ", " + competition_id);
				return false;
			}
		} else { /* new player , new competition */
			Map<Integer, Opponent> hm = new HashMap<Integer, Opponent>();
			hm.put(competition_id, new Opponent(player_id, competition_id));
			opponentsEstimations.put(player_id, hm);
		}
		return true;
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_Estimation#initNewCompetition()
	 */
	public boolean initNewCompetition() {

		int latestCompetitionId = GetterQueryExecution.getLatestCompetitionId();
		competitionId = latestCompetitionId;

		/** name, player_id */
		Map<Integer, String> opponentsAtCompetition = GetterQueryExecution.getPlayersInCompetition(competitionId);
		DebugOut.showVerboseEstimation(DebugOut.mapToString(opponentsAtCompetition));
		
		List<Integer> tmp = new ArrayList<Integer>();

		//		for (String string : opponentsAtCompetition.keySet()) {
		for (Integer playerId : opponentsEstimations.keySet()) {
			/*
			 * cleanup opponentsEstimations by not participating estimated players
			 * do not estimate myself
			 */
			if (!opponentsAtCompetition.containsKey(playerId)) {
				tmp.add(playerId);	/* prevent concurrent modification */
			} else {
				if (!opponentsAtCompetition.get(playerId).equals(myName)) {
					DebugOut.showVerboseEstimation("addOpponentWithCompetition.playerId.competitionId: " + playerId + ", " + competitionId);
					if(!addOpponentWithCompetition(playerId, competitionId)) {
						return false;
					}					
				}
			}
		}
		/* remove entries from opponentsEstimations */
		for (Integer integer : tmp) {
			opponentsEstimations.remove(integer);
			// DebugOut.showVerboseEstimation("opponentsEstimations.removed.playerId: " + integer + ", size(): " + opponentsEstimations.size());
		}
		// DebugOut.showVerboseEstimation("opponentsEstimations.size() after removal: " + opponentsEstimations.size());				
		// DebugOut.showVerboseEstimation(DebugOut.iterateHashMap(opponentsEstimations));
		
		return true;
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_Estimation#estimate()
	 */
	public boolean estimate() { 

//		DebugOut.showVerboseEstimation(opponentsEstimations.toString());
//		DebugOut.showVerboseEstimation(DebugOut.iterateHashMap(opponentsEstimations));
		
		for (Integer playerId : opponentsEstimations.keySet()) {
//			DebugOut.showVerboseEstimation("opponentsEstimations.size(): " + opponentsEstimations.size());
			DebugOut.showVerboseEstimation("opponentsEstimations.get.playerId.competitionId: " + playerId + ", " + competitionId);
			opponentsEstimations.get(playerId).get(competitionId).estimate();
		}

		return true;
	}


	/* (non-Javadoc)
	 * @see bot.module.th.estimation.IF_Estimation#getOpponentsEstimations()
	 */
	public Map<Integer, Map<Integer, Opponent>> getOpponentsEstimations() {
		return opponentsEstimations;
	}
}
